﻿/**
 * @file audio_opus.h
 * Copyright (c) Gaaagaa. All rights reserved.
 * 
 * @author  : Gaaagaa
 * @date    : 2020-03-05
 * @version : 1.0.0.0
 * @brief   : 封装 libopus 的 API，实现 PCM 音频数据的 opus 编解码操作。
 */

#ifndef __AUDIO_OPUS_H__
#define __AUDIO_OPUS_H__

#include "xtypes.h"

////////////////////////////////////////////////////////////////////////////////

/**
 * @enum  xopus_pcm_t
 * @brief OPUS 编解码操作所支持的 PCM 格式。
 */
typedef enum xopus_pcm_t
{
    XOPUS_PCM_S16  = 0x0010,  ///< PCM-S16
    XOPUS_PCM_F32  = 0x0020,  ///< PCM-F32
} xopus_pcm_t;

/**
 * @enum  xopus_samplerate_t
 * @brief OPUS 编解码支持的采样率。
 */
typedef enum xopus_samplerate_t
{
    XOPUS_SAMPRATE__8K =  8000,  ///< sample rate  8000
    XOPUS_SAMPRATE_12K = 12000,  ///< sample rate 12000
    XOPUS_SAMPRATE_16K = 16000,  ///< sample rate 16000
    XOPUS_SAMPRATE_24K = 24000,  ///< sample rate 24000
    XOPUS_SAMPRATE_48K = 48000,  ///< sample rate 48000
} xopus_samplerate_t;

////////////////////////////////////////////////////////////////////////////////
// opus_encoder_t

/**
 * @enum  xopus_enc_config_t
 * @brief Opus 编码操作使用的配置参数枚举值。
 */
typedef enum xopus_enc_config_t
{
    // audio bandwidth (from narrowband to fullband); default: depend on the sampling rate
    XOPUS_ENC_BANDWIDTH_NB     = 0x00000001,  ///< bandwidth :  4 kHz bandpass, narrow band
    XOPUS_ENC_BANDWIDTH_MB     = 0x00000002,  ///< bandwidth :  6 kHz bandpass, medium band
    XOPUS_ENC_BANDWIDTH_WB     = 0x00000003,  ///< bandwidth :  8 kHz bandpass, wide band
    XOPUS_ENC_BANDWIDTH_SWB    = 0x00000004,  ///< bandwidth : 12 kHz bandpass, super wide band
    XOPUS_ENC_BANDWIDTH_FB     = 0x00000005,  ///< bandwidth : 20 kHz bandpass, full band
    XOPUS_ENC_BANDWIDTH_MASK   = 0x0000000F,  ///< bit mask

    // pcm input type
    XOPUS_ENC_INPUT_PCM_S16    = 0x00000010,  ///< PCM-S16 (default)
    XOPUS_ENC_INPUT_PCM_F32    = 0x00000020,  ///< PCM-F32
    XOPUS_ENC_INPUT_PCM_MASK   = 0x00000030,  ///< bit mask

    // application type
    XOPUS_ENC_APP_VOIP         = 0x00000040,  ///< for VoIP/videoconference applications
    XOPUS_ENC_APP_AUDIO        = 0x00000080,  ///< for broadcast/high-fidelity applications (default)
    XOPUS_ENC_APP_LOWDELAY     = 0x000000C0,  ///< for lowest-achievable latency
    XOPUS_ENC_APP_MASK         = 0x000000C0,  ///< bit mask

    // control frame rate
    XOPUS_ENC_FRAMEMS_2P5      = 0x00000100,  ///< Output frames at intervals of 2.5 ms
    XOPUS_ENC_FRAMEMS_5        = 0x00000200,  ///< Output frames at intervals of   5 ms
    XOPUS_ENC_FRAMEMS_10       = 0x00000300,  ///< Output frames at intervals of  10 ms
    XOPUS_ENC_FRAMEMS_20       = 0x00000400,  ///< Output frames at intervals of  20 ms (default)
    XOPUS_ENC_FRAMEMS_40       = 0x00000500,  ///< Output frames at intervals of  40 ms
    XOPUS_ENC_FRAMEMS_60       = 0x00000600,  ///< Output frames at intervals of  60 ms
    XOPUS_ENC_FRAMEMS_80       = 0x00000700,  ///< Output frames at intervals of  80 ms
    XOPUS_ENC_FRAMEMS_100      = 0x00000800,  ///< Output frames at intervals of 100 ms
    XOPUS_ENC_FRAMEMS_120      = 0x00000900,  ///< Output frames at intervals of 120 ms
    XOPUS_ENC_FRAMEMS_MASK     = 0x00000F00,  ///< bit mask

    // complexity
    XOPUS_ENC_COMPLEXITY_0     = 0x00001000,  ///< complexity 0
    XOPUS_ENC_COMPLEXITY_1     = 0x00002000,  ///< complexity 1
    XOPUS_ENC_COMPLEXITY_2     = 0x00003000,  ///< complexity 2
    XOPUS_ENC_COMPLEXITY_3     = 0x00004000,  ///< complexity 3
    XOPUS_ENC_COMPLEXITY_4     = 0x00005000,  ///< complexity 4
    XOPUS_ENC_COMPLEXITY_5     = 0x00006000,  ///< complexity 5
    XOPUS_ENC_COMPLEXITY_6     = 0x00007000,  ///< complexity 6
    XOPUS_ENC_COMPLEXITY_7     = 0x00008000,  ///< complexity 7
    XOPUS_ENC_COMPLEXITY_8     = 0x00009000,  ///< complexity 8
    XOPUS_ENC_COMPLEXITY_9     = 0x0000A000,  ///< complexity 9
    XOPUS_ENC_COMPLEXITY_A     = 0x0000B000,  ///< complexity 10 (default)
    XOPUS_ENC_COMPLEXITY_MASK  = 0x0000F000,  ///< bit mask

    XOPUS_ENC_ENABLE_CBR       = 0x00010000,  ///< enable constant bitrate; default: variable bitrate
    XOPUS_ENC_ENABLE_CVBR      = 0x00020000,  ///< enable constrained variable bitrate; default: unconstrained
    XOPUS_ENC_DELAYED_DECISION = 0x00040000,  ///< use look-ahead for speech/music detection (experts only); default: disabled
    XOPUS_ENC_ENABLE_INBANDFEC = 0x00080000,  ///< enable SILK inband FEC; default: disabled
    XOPUS_ENC_FORCE_MONO       = 0x00100000,  ///< force mono encoding, even for stereo input; default: auto
    XOPUS_ENC_ENABLE_DTX       = 0x00200000,  ///< enable SILK DTX(discontinuous transmission); default: disabled

    // simulate packet loss, in percent (0-100)
    XOPUS_ENC_LOSSPERCENT_MIN  = 0x00000000,  ///< loss percent : 0 (default)
    XOPUS_ENC_LOSSPERCENT_MAX  = 0x64000000,  ///< loss percent : 100
    XOPUS_ENC_LOSSPERCENT_MASK = 0xFF000000,  ///< bit mask
    XOPUS_ENC_LOSSPERCENT_MBIT = 24,          ///< bit shift

    XOPUS_ENC_DEFAULT = XOPUS_ENC_INPUT_PCM_S16   |
                        XOPUS_ENC_APP_AUDIO       |
                        XOPUS_ENC_FRAMEMS_20      |
                        XOPUS_ENC_COMPLEXITY_A    |
                        XOPUS_ENC_LOSSPERCENT_MIN
} xopus_enc_config_t;

//======================================

#define XOPUS_ENC_BITMODE(xencfg, xbit)  (0 != ((xencfg) & (xbit)))
#define XOPUS_ENC_GET_BIT(xencfg, xbit)  ((xencfg) & (xbit))
#define XOPUS_ENC_SET_BIT(xencfg, xbit)  ((xencfg) = ((xencfg) |  (xbit)))
#define XOPUS_ENC_DEL_BIT(xencfg, xbit)  ((xencfg) = ((xencfg) & ~(xbit)))

//======================================
// band width

#define XOPUS_ENC_GET_BW(xcfg)        ((xcfg) & XOPUS_ENC_BANDWIDTH_MASK)
#define XOPUS_ENC_SET_BW(xcfg, xexp)  (xcfg) = (((xcfg) & ~XOPUS_ENC_BANDWIDTH_MASK) | (xexp))

//======================================
// pcm input type

#define XOPUS_ENC_GET_PCM(xcfg)       ((xcfg) & XOPUS_ENC_INPUT_PCM_MASK)
#define XOPUS_ENC_SET_PCM(xcfg, xexp) (xcfg) = (((xcfg) & ~XOPUS_ENC_INPUT_PCM_MASK) | (xexp))

//======================================
// application type

#define XOPUS_ENC_GET_APP(xcfg)       ((xcfg) & XOPUS_ENC_APP_MASK)
#define XOPUS_ENC_SET_APP(xcfg, xexp) (xcfg) = (((xcfg) & ~XOPUS_ENC_APP_MASK) | (xexp))

//======================================
// frame rate

#define XOPUS_ENC_GET_FMS(xcfg)       ((xcfg) & XOPUS_ENC_FRAMEMS_MASK)
#define XOPUS_ENC_SET_FMS(xcfg, xexp) (xcfg) = (((xcfg) & ~XOPUS_ENC_FRAMEMS_MASK) | (xexp))

//======================================
// complexity

#define XOPUS_ENC_GET_CP(xcfg)        ((xcfg) & XOPUS_ENC_COMPLEXITY_MASK)
#define XOPUS_ENC_SET_CP(xcfg, xexp)  (xcfg) = (((xcfg) & ~XOPUS_ENC_COMPLEXITY_MASK) | (xexp))

//======================================
// simulate packet loss, in percent

#define XOPUS_ENC_GET_LP(xcfg) \
    (((xcfg) & XOPUS_ENC_LOSSPERCENT_MASK) >> XOPUS_ENC_LOSSPERCENT_MBIT)

#define XOPUS_ENC_SET_LP(xcfg, xexp)                          \
    do                                                        \
    {                                                         \
        if ((xexp) <= 0)                                      \
            (xcfg) = ((xcfg) & ~XOPUS_ENC_LOSSPERCENT_MASK);  \
        else if ((xexp) > 100)                                \
            (xcfg) = ((xcfg) & ~XOPUS_ENC_LOSSPERCENT_MASK) | \
                     XOPUS_ENC_LOSSPERCENT_MAX;               \
        else                                                  \
            (xcfg) = ((xcfg) & ~XOPUS_ENC_LOSSPERCENT_MASK) | \
                     ((xexp) << XOPUS_ENC_LOSSPERCENT_MBIT);  \
    } while (0)

//======================================

/**
 * @class opus_encoder_t
 * @brief 实现 PCM 音频数据的 opus 编码操作。
 */
class opus_encoder_t
{
    // common data types
public:
    typedef x_byte_t *        xopusbuf_t;
    typedef const xopusbuf_t  copusbuf_t;
    typedef void *            xvpcmbuf_t;
    typedef const void *      cvpcmbuf_t;

    /**
     * @enum  xerrno_table_t
     * @brief 错误码表。
     */
    typedef enum xerrno_table_t
    {
        //======================================
        // open() 接口产生的错误码

        X_ERR_INVALID_CHANNELS    = 0x00000001,  ///< 通道数量只能是 1 或 2
        X_ERR_INVALID_SAMPRATE    = 0x00000002,  ///< 不支持该采样率的编码操作
        X_ERR_INVALID_APPTYPE     = 0x00000003,  ///< 不支持该 APP 类型
        X_ERR_INVALID_PCMTYPE     = 0x00000004,  ///< 不支持该 PCM 类型
        X_ERR_CREATE_ENCODER      = 0x00000005,  ///< 创建编码器对象失败

        X_ERR_ECTL_LOWER_CODE     = 0x00000100,  ///< 参数设置操作的错误码下限值
        X_ERR_ECTL_SET_BITRATE    = 0x00000101,  ///< bitrate
        X_ERR_ECTL_SET_BANDWIDTH  = 0x00000102,  ///< bandwidth
        X_ERR_ECTL_SET_CBR        = 0x00000103,  ///< cbr
        X_ERR_ECTL_SET_CVBR       = 0x00000104,  ///< cvbr
        X_ERR_ECTL_SET_COMPLEXITY = 0x00000105,  ///< complexity
        X_ERR_ECTL_SET_INBAND_FEC = 0x00000106,  ///< inband fec
        X_ERR_ECTL_SET_FORCE_MONO = 0x00000107,  ///< force mono
        X_ERR_ECTL_SET_DTX        = 0x00000108,  ///< dtx
        X_ERR_ECTL_SET_LOSS_PERC  = 0x00000109,  ///< packet loss percent
        X_ERR_ECTL_SET_LSB_DEPTH  = 0x0000010A,  ///< lsb depth
        X_ERR_ECTL_SET_FDURATION  = 0x0000010B,  ///< frame duration
        X_ERR_ECTL_UPPER_CODE     = 0x000001FF,  ///< 参数设置操作的错误码上限值

        //======================================
        // encode() 接口产生的错误码

        X_ERR_ENCODE_UNOPEN       = 0x00000401,  ///< 编码器未打开
        X_ERR_ENCODE_ARGS         = 0x00000402,  ///< 输入参数有误
        X_ERR_ENCODE_ENCS16       = 0x00000403,  ///< 执行 PCM-S16 编码时产生错误
        X_ERR_ENCODE_ENCF32       = 0x00000404,  ///< 执行 PCM-F32 编码时产生错误
        X_ERR_ENCODE_PACKET       = 0x00000405,  ///< 编码输出的 OPUS 数据包格式错误

        //======================================
    } xerrno_table_t;

    // common invoking
public:
    /**********************************************************/
    /**
     * @brief 获取 xerrno_table_t 中的错误码名称。
     */
    static x_cstring_t xerrno_name(x_int32_t xit_errno)
    {
        x_cstring_t xszt_name = "";

        switch (xit_errno)
        {
        //======================================
        // open() 接口产生的错误码

        case X_ERR_INVALID_CHANNELS    : xszt_name = "X_ERR_INVALID_CHANNELS"   ; break;
        case X_ERR_INVALID_SAMPRATE    : xszt_name = "X_ERR_INVALID_SAMPRATE"   ; break;
        case X_ERR_INVALID_APPTYPE     : xszt_name = "X_ERR_INVALID_APPTYPE"    ; break;
        case X_ERR_INVALID_PCMTYPE     : xszt_name = "X_ERR_INVALID_PCMTYPE"    ; break;
        case X_ERR_CREATE_ENCODER      : xszt_name = "X_ERR_CREATE_ENCODER"     ; break;

        case X_ERR_ECTL_LOWER_CODE     : xszt_name = "X_ERR_ECTL_LOWER_CODE"    ; break;
        case X_ERR_ECTL_SET_BITRATE    : xszt_name = "X_ERR_ECTL_SET_BITRATE"   ; break;
        case X_ERR_ECTL_SET_BANDWIDTH  : xszt_name = "X_ERR_ECTL_SET_BANDWIDTH" ; break;
        case X_ERR_ECTL_SET_CBR        : xszt_name = "X_ERR_ECTL_SET_CBR"       ; break;
        case X_ERR_ECTL_SET_CVBR       : xszt_name = "X_ERR_ECTL_SET_CVBR"      ; break;
        case X_ERR_ECTL_SET_COMPLEXITY : xszt_name = "X_ERR_ECTL_SET_COMPLEXITY"; break;
        case X_ERR_ECTL_SET_INBAND_FEC : xszt_name = "X_ERR_ECTL_SET_INBAND_FEC"; break;
        case X_ERR_ECTL_SET_FORCE_MONO : xszt_name = "X_ERR_ECTL_SET_FORCE_MONO"; break;
        case X_ERR_ECTL_SET_DTX        : xszt_name = "X_ERR_ECTL_SET_DTX"       ; break;
        case X_ERR_ECTL_SET_LOSS_PERC  : xszt_name = "X_ERR_ECTL_SET_LOSS_PERC" ; break;
        case X_ERR_ECTL_SET_LSB_DEPTH  : xszt_name = "X_ERR_ECTL_SET_LSB_DEPTH" ; break;
        case X_ERR_ECTL_SET_FDURATION  : xszt_name = "X_ERR_ECTL_SET_FDURATION" ; break;
        case X_ERR_ECTL_UPPER_CODE     : xszt_name = "X_ERR_ECTL_UPPER_CODE"    ; break;

        //======================================
        // encode() 接口产生的错误码

        case X_ERR_ENCODE_UNOPEN       : xszt_name = "X_ERR_ENCODE_UNOPEN"      ; break;
        case X_ERR_ENCODE_ARGS         : xszt_name = "X_ERR_ENCODE_ARGS"        ; break;
        case X_ERR_ENCODE_ENCS16       : xszt_name = "X_ERR_ENCODE_ENCS16"      ; break;
        case X_ERR_ENCODE_ENCF32       : xszt_name = "X_ERR_ENCODE_ENCF32"      ; break;
        case X_ERR_ENCODE_PACKET       : xszt_name = "X_ERR_ENCODE_PACKET"      ; break;

        //======================================
        default: break;
        }

        return xszt_name;
    }

    /**********************************************************/
    /**
     * @brief 获取 xerrno_table_t 中的错误码名称。
     */
    static x_cstring_t xerrno_name(x_errno_t xerr_no)
    {
        return xerrno_name(XERR_HINO(xerr_no));
    }

    // constructor/destuctor
public:
    opus_encoder_t(void);
    ~opus_encoder_t(void);

    // public interfaces
public:
    /**********************************************************/
    /**
     * @brief 打开编码器对象。
     * 
     * @param [in ] xut_channels : 通道数量（仅支持 1 或 2）。
     * @param [in ] xut_samprate : 采样率（仅支持 xopus_samplerate_t 列举的枚举值）。
     * @param [in ] xut_apptype  : 应用类型（参看 xopus_app_t）。
     * @param [in ] xut_config   : 编码操作的配置选项（使用 xopus_enc_config_t 的组合值）。
     * @param [in ] xut_bitrate  : 编码输出的位速。
     * 
     * @return x_errno_t : 错误码。
     * @retval X_ERR_OK, 操作成功。
     * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
     * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
     */
    x_errno_t open(
        x_uint16_t xut_channels,
        x_uint32_t xut_samprate,
        x_uint32_t xut_config  = XOPUS_ENC_DEFAULT,
        x_uint32_t xut_bitrate = 0);

    /**********************************************************/
    /**
     * @brief 关闭编码器对象。
     */
    x_void_t close(void);

    /**********************************************************/
    /**
     * @brief 判断当前编码器是否已经打开。
     */
    inline x_bool_t is_open(void) const
    {
        return (X_NULL != m_xht_encoder);
    }

    /**********************************************************/
    /**
     * @brief 每次编码输入的每帧 PCM 音频所需字节数。
     */
    x_uint32_t frame_len(void) const;

    /**********************************************************/
    /**
     * @brief 执行 PCM-S16/PCM-F32 音频数据编码操作。
     * @note 
     * 输入的音频帧 xbuf_vpcm 格式必须与编码器使用的类型一致，
     * 即可以是 S16（16位有符号整型）或 F32（32位浮点型），
     * 缓存的有效数据字节数为 frame_len()，若输入时不足，请用 0 填充。
     * 
     * @param [out   ] xbuf_opus : 输出编码后的音频数据所使用的缓存。
     * @param [in,out] xut_osize : 入参，xbuf_opus 缓存大小（建议 >= 1024）；
     *                             回参，操作成功时输出编码后的有效数据大小。
     * @param [in    ] xbuf_vpcm : 输入的 PCM 音频帧数据。
     * @param [in,out] xut_fsize : 入参，xbuf_vpcm 缓存输入的有效数据字节数（必须 == frame_len()）；
     *                             回参，操作成功时输出编码后的有效数据字节数。
     * 
     * @return x_errno_t : 错误码。
     * @retval X_ERR_OK, 操作成功。
     * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
     * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
     */
    x_errno_t encode(
                xopusbuf_t   xbuf_opus,
                x_uint32_t & xut_osize,
                cvpcmbuf_t   xbuf_vpcm,
                x_uint32_t & xut_fsize);

    // data members
protected:
    x_handle_t  m_xht_encoder ;  ///< 编码器对象的操作句柄
    x_uint16_t  m_xut_epcmtype;  ///< PCM-S16 or PCM-F32，默认 PCM-S16
    x_uint16_t  m_xut_channels;  ///< 编码操作的通道数
    x_uint32_t  m_xut_samprate;  ///< 编码操作的音频采样率
    x_uint32_t  m_xut_fsamples;  ///< 单通道，每帧的音频采样数
};

////////////////////////////////////////////////////////////////////////////////
// opus_decoder_t

/**
 * @class opus_decoder_t
 * @brief 实现 PCM 音频数据的 opus 解码操作。
 */
class opus_decoder_t
{
    // common data types
public:
    typedef x_byte_t *        xopusbuf_t;
    typedef const xopusbuf_t  copusbuf_t;
    typedef void *            xvpcmbuf_t;
    typedef const void *      cvpcmbuf_t;

    /**
     * @enum  xerrno_table_t
     * @brief 错误码表。
     */
    typedef enum xerrno_table_t
    {
        //======================================
        // open() 接口产生的错误码

        X_ERR_INVALID_PCMTYPE     = 0x00000001,  ///< 不支持该 PCM 类型
        X_ERR_INVALID_CHANNELS    = 0x00000002,  ///< 通道数量只能是 1 或 2
        X_ERR_INVALID_SAMPRATE    = 0x00000003,  ///< 不支持该采样率的解码操作
        X_ERR_CREATE_DECODER      = 0x00000004,  ///< 创建解码器对象失败

        //======================================
        // decode() 接口产生的错误码

        X_ERR_DECODE_UNOPEN       = 0x00000401,  ///< 解码器未打开
        X_ERR_DECODE_ARGS         = 0x00000402,  ///< 输入参数有误
        X_ERR_DECODE_DECS16       = 0x00000403,  ///< 执行 PCM-S16 解码时产生错误
        X_ERR_DECODE_DECF32       = 0x00000404,  ///< 执行 PCM-F32 解码时产生错误

        //======================================
    } xerrno_table_t;

    // common invoking
public:
    /**********************************************************/
    /**
     * @brief 获取 xerrno_table_t 中的错误码名称。
     */
    static x_cstring_t xerrno_name(x_int32_t xit_errno)
    {
        x_cstring_t xszt_name = "";

        switch (xit_errno)
        {
        //======================================
        // open() 接口产生的错误码

        case X_ERR_INVALID_PCMTYPE  : xszt_name = "X_ERR_INVALID_PCMTYPE" ; break;
        case X_ERR_INVALID_CHANNELS : xszt_name = "X_ERR_INVALID_CHANNELS"; break;
        case X_ERR_INVALID_SAMPRATE : xszt_name = "X_ERR_INVALID_SAMPRATE"; break;
        case X_ERR_CREATE_DECODER   : xszt_name = "X_ERR_CREATE_DECODER"  ; break;

        //======================================
        // decode() 接口产生的错误码

        case X_ERR_DECODE_UNOPEN    : xszt_name = "X_ERR_DECODE_UNOPEN"   ; break;
        case X_ERR_DECODE_ARGS      : xszt_name = "X_ERR_DECODE_ARGS"     ; break;
        case X_ERR_DECODE_DECS16    : xszt_name = "X_ERR_DECODE_DECS16"   ; break;
        case X_ERR_DECODE_DECF32    : xszt_name = "X_ERR_DECODE_DECF32"   ; break;

        //======================================
        default: break;
        }

        return xszt_name;
    }

    /**********************************************************/
    /**
     * @brief 获取 xerrno_table_t 中的错误码名称。
     */
    static x_cstring_t xerrno_name(x_errno_t xerr_no)
    {
        return xerrno_name(XERR_HINO(xerr_no));
    }

    // constructor/destuctor
public:
    opus_decoder_t(void);
    ~opus_decoder_t(void);

    // public interfaces
public:
    /**********************************************************/
    /**
     * @brief 打开解码器对象。
     * 
     * @param [in ] xut_epcmtype : 解码操作的 PCM 音频格式（PCM-S16/PCM-F32）。
     * @param [in ] xut_channels : 解码操作的通道数。
     * @param [in ] xut_samprate : 解码操作的音频采样率。
     * 
     * @return x_errno_t : 错误码。
     * @retval X_ERR_OK, 操作成功。
     * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
     * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
     */
    x_errno_t open(
        x_uint16_t xut_epcmtype,
        x_uint16_t xut_channels,
        x_uint32_t xut_samprate);

    /**********************************************************/
    /**
     * @brief 关闭解码器对象。
     */
    x_void_t close(void);

    /**********************************************************/
    /**
     * @brief 判断解码器对象是否已经打开。
     */
    inline x_bool_t is_open(void) const
    {
        return (X_NULL != m_xht_decoder);
    }

    /**********************************************************/
    /**
     * @brief 执行解码操作。
     * 
     * @param [out   ] xbuf_vpcm : 输出解码后的 PCM-S16/PCM-F32 音频数据所使用的缓存。
     * @param [in,out] xut_fsize : 入参，xbuf_pcm 缓存大小（按字节计数）；
     *                             回参，xbuf_pcm 中有效数据长度（按字节计数）。
     * @param [in    ] xbuf_opus : 输入待解码的 OPUS 音频数据的缓存。
     * @param [in    ] xut_osize : xbuf_opus 缓存中有效数据长度（按字节计数）。
     * 
     * @return x_errno_t : 错误码。
     * @retval X_ERR_OK, 操作成功。
     * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
     * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
     */
    x_errno_t decode(
                xvpcmbuf_t   xbuf_vpcm,
                x_uint32_t & xut_fsize,
                copusbuf_t   xbuf_opus,
                x_uint32_t   xut_osize);

    // data members
protected:
    x_handle_t  m_xht_decoder ;  ///< 解码器对象句柄
    x_uint16_t  m_xut_epcmtype;  ///< 解码操作的 PCM 音频格式（PCM-S16/PCM-F32）
    x_uint16_t  m_xut_channels;  ///< 解码操作的通道数
    x_uint32_t  m_xut_samprate;  ///< 解码操作的音频采样率
};

////////////////////////////////////////////////////////////////////////////////

#endif // __AUDIO_OPUS_H__

